package user

import (
	"AccessAdmin/internal/consts"
	"AccessAdmin/internal/dao"
	"AccessAdmin/internal/model"
	"AccessAdmin/internal/model/entity"
	"AccessAdmin/internal/service"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/guid"
)

type sUser struct {
}

func init() {
	service.RegisterUser(New())
}

func New() *sUser {
	return &sUser{}
}

// Login 执行登录
func (s *sUser) Login(ctx context.Context, in model.UserLoginInput) error {
	userEntity, err := s.GetUserByPassportAndPassword(
		ctx,
		in.Username,
		s.EncryptPassword(in.Password),
	)
	if err != nil {
		return err
	}
	if userEntity == nil {
		return gerror.New(`账号或密码错误`)
	}
	if userEntity.Status != consts.StatusEnable {
		return gerror.New(`此账号已被禁用，请联系管理员处理！`)
	}
	if err := service.Session().SetUser(ctx, userEntity); err != nil {
		return err
	}
	menu, route, err := service.Menu().GetMenuByUserId(ctx, userEntity.Id)
	if err != nil {
		return err
	}
	// 设置菜单信息
	err = service.Session().SetMenus(ctx, menu)
	if err != nil {
		return err
	}
	// 设置路由信息
	err = service.Session().SetRoutes(ctx, route)
	if err != nil {
		return err
	}
	// 自动更新上线
	service.BizCtx().SetUser(ctx, userEntity)

	AccessToken := guid.S([]byte(userEntity.Username))
	LastLoginDevice := in.LoginDevice + "/" + in.LoginBrowser
	// 更新用户信息
	r := g.RequestFromCtx(ctx)
	_, err = dao.UserAdmin.Ctx(ctx).
		Data(g.Map{
			dao.UserAdmin.Columns().LastLoginIp:     r.GetClientIp(),
			dao.UserAdmin.Columns().LastLoginTime:   gtime.Now().Format("Y-m-d H:i:s"),
			dao.UserAdmin.Columns().LastLoginDevice: LastLoginDevice,
			dao.UserAdmin.Columns().AccessToken:     AccessToken,
		}).Where(dao.UserAdmin.Columns().Id, userEntity.Id).Update()
	if err != nil {
		return err
	}

	r.Cookie.Set(consts.LoginUniqueKey, AccessToken)

	return nil
}

// Logout 登录注销
func (s *sUser) Logout(ctx context.Context) error {
	err := service.Session().RemoveMenus(ctx)
	if err != nil {
		return err
	}
	err = service.Session().RemoveRoutes(ctx)
	if err != nil {
		return err
	}
	return service.Session().RemoveUser(ctx)
}

// GetList 获取用户列表
func (s *sUser) GetList(ctx context.Context, in model.UserGetListInput) (out *model.UserGetListOutput, err error) {
	out = &model.UserGetListOutput{
		Page: in.Page,
		Size: in.Size,
	}
	m := dao.UserAdmin.Ctx(ctx)
	if in.Username != "" {
		m = m.WhereLike(dao.UserAdmin.Columns().Username, in.Username)
	}
	if in.Status > 0 {
		m = m.Where(dao.UserAdmin.Columns().Status, in.Status-1)
	}
	out.Total, err = m.Count()
	if err != nil {
		return out, err
	}
	err = m.Page(in.Page, in.Size).OrderDesc(dao.UserAdmin.Columns().Id).Scan(&out.List)
	if err != nil {
		return nil, err
	}
	if len(out.List) == 0 {
		return nil, nil
	}
	return out, nil
}

// GetDetail 获取用户详情
func (s *sUser) GetDetail(ctx context.Context, in model.UserGetDetailInput) (out *model.UserGetDetailOutput, err error) {
	out = &model.UserGetDetailOutput{}
	err = dao.UserAdmin.Ctx(ctx).Where(dao.UserAdmin.Columns().Id, in.Id).Scan(&out)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Create 创建用户
func (s *sUser) Create(ctx context.Context, in model.UserCreateInput) (out *model.UserCreateOutput, err error) {
	tx, err := dao.UserAdmin.DB().Begin(ctx)
	if err != nil {
		return nil, err
	}
	// 方法退出时检验返回值，
	// 如果结果成功则执行tx.Commit()提交,
	// 否则执行tx.Rollback()回滚操作。
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	in.Password = s.EncryptPassword(in.Password)
	lastInsertID, err := dao.UserAdmin.Ctx(ctx).Data(in).InsertAndGetId()
	if err != nil {
		return out, err
	}

	err = s.InsertUserRoles(ctx, lastInsertID, in.Username, in.Roles)
	if err != nil {
		return nil, err
	}

	return &model.UserCreateOutput{UserId: uint(lastInsertID)}, err
}

// Update 修改
func (s *sUser) Update(ctx context.Context, in model.UserUpdateInput) error {
	return dao.UserAdmin.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		var err error
		m := dao.UserAdmin.Ctx(ctx).Data(in).FieldsEx(dao.UserAdmin.Columns().Id).Where(dao.UserAdmin.Columns().Id, in.Id)
		if in.Password != "" {
			in.Password = s.EncryptPassword(in.Password)
			_, err = m.Update()
		} else {
			_, err = m.FieldsEx(dao.UserAdmin.Columns().Password).Update()
		}
		if err != nil {
			return err
		}
		err = s.InsertUserRoles(ctx, int64(in.Id), in.Username, in.Roles)
		if err != nil {
			return err
		}
		return nil
	})
}

// UpdatePassword 修改密码
func (s *sUser) UpdatePassword(ctx context.Context, in model.UserUpdatePasswordInput) error {
	if in.NewPassword != in.RepPassword {
		return gerror.New(`新密码和重复密码错误！`)
	}
	var user *entity.UserAdmin
	err := dao.UserAdmin.Ctx(ctx).Where(dao.UserAdmin.Columns().Id, service.BizCtx().Get(ctx).User.Id).Scan(&user)
	if err != nil {
		return err
	}
	if user.Password != s.EncryptPassword(in.OldPassword) {
		return gerror.New(`原密码错误！`)
	}
	_, err = dao.UserAdmin.Ctx(ctx).Data(g.Map{
		dao.UserAdmin.Columns().Password: s.EncryptPassword(in.NewPassword),
	}).FieldsEx(dao.UserAdmin.Columns().Id).Where(dao.UserAdmin.Columns().Id, user.Id).Update()
	if err != nil {
		return err
	}
	return nil
}

// InsertUserRoles 插入角色
func (s *sUser) InsertUserRoles(ctx context.Context, UserId int64, Username string, Roles []uint) error {
	_, err := dao.UserRole.Ctx(ctx).Where(dao.UserRole.Columns().UserId, UserId).Delete()
	if err != nil {
		return err
	}
	e := service.Casbin().CasBin(ctx)
	_, err = e.DeleteRolesForUser(Username)
	if err != nil {
		return err
	}
	if len(Roles) > 0 {
		var roleData []*entity.Role
		var insertItems g.List
		var roles []string
		err := dao.Role.Ctx(ctx).WhereIn(dao.Role.Columns().Id, Roles).Scan(&roleData)
		fmt.Println(err)
		if err != nil {
			return err
		}
		for _, item := range roleData {
			insertItems = append(insertItems, g.Map{
				dao.UserRole.Columns().UserId: UserId,
				dao.UserRole.Columns().RoleId: item.Id,
			})
			roles = append(roles, item.Name)
		}
		_, err = dao.UserRole.Ctx(ctx).Data(insertItems).Insert()
		if err != nil {
			return err
		}
		_, err = e.AddRolesForUser(Username, roles)
		if err != nil {
			return err
		}
		fmt.Println("更新角色权限关系", e.GetGroupingPolicy())
	}
	return nil
}

// UpdateInfo 修改个人信息
func (s *sUser) UpdateInfo(ctx context.Context, in model.UserUpdateInfoInput) error {
	userEntity := service.Session().GetUser(ctx)
	userModel, err := service.User().GetDetail(ctx, model.UserGetDetailInput{
		Id: userEntity.Id,
	})
	if err != nil {
		return err
	}
	userUpdate := g.Map{
		dao.UserAdmin.Columns().RealName: in.RealName,
		dao.UserAdmin.Columns().Phone:    in.Phone,
		dao.UserAdmin.Columns().Email:    in.Email,
		dao.UserAdmin.Columns().Remark:   in.Remark,
	}
	// 如果修改头像
	if in.IsUpdateAvatar && in.AvatarUrl != "" {
		avatarUrl, err := service.Debris().Base64ToImage(ctx, in.AvatarUrl)
		if err != nil {
			return err
		}
		userUpdate[dao.UserAdmin.Columns().AvatarUrl] = avatarUrl
	}
	_, err = dao.UserAdmin.Ctx(ctx).Data(userUpdate).FieldsEx(dao.UserAdmin.Columns().Id).Where(dao.UserAdmin.Columns().Id, userModel.Id).Update()
	if err != nil {
		return err
	}
	if userModel.AvatarUrl != "" {
		service.Attachment().DeleteFile(ctx, userModel.AvatarUrl)
	}

	// 自动更新上线
	newUserModel := entity.UserAdmin{}
	_ = dao.UserAdmin.Ctx(ctx).Where(dao.UserAdmin.Columns().Id, userModel.Id).Scan(&newUserModel)
	_ = service.Session().SetUser(ctx, &newUserModel)
	service.BizCtx().SetUser(ctx, &newUserModel)
	return nil
}

// LoginLog 登录日志记录
func (s *sUser) LoginLog(ctx context.Context, in model.UserLoginLogInput) error {
	in.LoginTime = gtime.Datetime()
	_, err := dao.AuthLoginLogs.Ctx(ctx).Data(&in).Insert()
	if err != nil {
		return err
	}
	return nil
}

// IsCtxAdmin 当前用户是否管理员
func (s *sUser) IsCtxAdmin(ctx context.Context) bool {
	var ctxUser = service.BizCtx().Get(ctx).User
	return s.IsAdmin(ctx, uint(ctxUser.Id))
}

// IsAdmin 判断给定用户是否管理员
func (s *sUser) IsAdmin(ctx context.Context, userId uint) bool {
	adminIds := g.Cfg().MustGet(ctx, "setting.adminIds").Uints()
	for _, adminId := range adminIds {
		if userId == adminId {
			return true
		}
	}
	return false
}

// EncryptPassword 将密码按照内部算法进行加密
func (s *sUser) EncryptPassword(password string) string {
	return gmd5.MustEncrypt(password)
}

// GetUserByPassportAndPassword 根据账号和密码查询用户信息，一般用于账号密码登录。
// 注意password参数传入的是按照相同加密算法加密过后的密码字符串。
func (s *sUser) GetUserByPassportAndPassword(ctx context.Context, username, password string) (user *entity.UserAdmin, err error) {
	err = dao.UserAdmin.Ctx(ctx).Where(g.Map{
		dao.UserAdmin.Columns().Username: username,
		dao.UserAdmin.Columns().Password: password,
	}).Scan(&user)
	return
}
